//
// Created by guoshunw on 24-4-1.
//

#include "omx_callback_base.h"
#include "enum_to_string.h"
#include <OMX_Component.h>


namespace openmax::callback {
    OMXCallbackBase::OMXCallbackBase(test::AppContext *appCtx, OMX_STRING componentName, tsem_t *event_sem)
        : OMX_CALLBACKTYPE{[](OMX_OUT OMX_HANDLETYPE hComponent,
                              OMX_OUT OMX_PTR pAppData,
                              OMX_OUT OMX_EVENTTYPE eEvent,
                              OMX_OUT OMX_U32 nData1,
                              OMX_OUT OMX_U32 nData2,
                              OMX_OUT OMX_PTR pEventData) {
                               auto cbBase = static_cast<OMXCallbackBase *>(pAppData);
                               LOG_RETURN_VAL_IF_NULL(cbBase, OMX_ErrorBadParameter)
                               return cbBase->BaseEventHandler(hComponent, eEvent, nData1, nData2, pEventData);
                           },
                           [](OMX_OUT OMX_HANDLETYPE hComponent,
                              OMX_OUT OMX_PTR pAppData,
                              OMX_OUT OMX_BUFFERHEADERTYPE *pBuffer) {
                               auto cbBase = static_cast<OMXCallbackBase *>(pAppData);
                               LOG_RETURN_VAL_IF_NULL(cbBase, OMX_ErrorBadParameter)
                               return cbBase->BaseEmptyBufferDone(hComponent, pBuffer);
                           },
                           [](OMX_OUT OMX_HANDLETYPE hComponent,
                              OMX_OUT OMX_PTR pAppData,
                              OMX_OUT OMX_BUFFERHEADERTYPE *pBuffer) {
                               auto cbBase = static_cast<OMXCallbackBase *>(pAppData);
                               LOG_RETURN_VAL_IF_NULL(cbBase, OMX_ErrorBadParameter)
                               return cbBase->BaseFillBufferDone(hComponent, pBuffer);
                           }},
          app_ctx_(appCtx), component_name_(componentName), event_sem_(event_sem) {
    }
    OMX_ERRORTYPE OMXCallbackBase::BaseEventHandler(OMX_HANDLETYPE hComponent, OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2, OMX_PTR pEventData) {
        std::stringstream info;
        info << "Component " << component_name_ << " of handle: " << hComponent;

        if (OMX_EventError == eEvent) {
            LOGE << info.str() << " EventHandler error " << GetEnumName(static_cast<OMX_ERRORTYPE>(nData1)) << " nData2: " << nData2;
            return static_cast<OMX_ERRORTYPE>(nData1);
        }
        //        LOGI << "EventHandler for " << info.str() << ", event " << GetEnumName(eEvent) << " nData1: " << nData1 << " nData2: " << nData2;
        if (eEvent == OMX_EventCmdComplete) {
            auto event_cmd = static_cast<OMX_COMMANDTYPE>(nData1);
            if (OMX_CommandStateSet == event_cmd) {
                auto state = static_cast<OMX_STATETYPE>(nData2);
                switch (nData2) {
                    case OMX_StateInvalid:
                    case OMX_StateLoaded:
                    case OMX_StateIdle:
                    case OMX_StateExecuting:
                    case OMX_StatePause:
                    case OMX_StateWaitForResources: {
                        LOGI << info.str() << " state set to " << GetEnumName(state);
                        break;
                    }
                    default: {
                        LOGW << info.str() << "Unknown command state set: " << GetEnumName(static_cast<OMX_STATETYPE>(nData2));
                    } break;
                }

                tsem_up(event_sem_);
            } else if (OMX_CommandPortEnable == event_cmd || OMX_CommandPortDisable == event_cmd) {
                LOGI << info.str() << "Command port enable or disable: "
                     << ", nData2: " << nData2;
                tsem_up(event_sem_);
            }

        } else if (OMX_EventPortSettingsChanged == eEvent) {
            LOGI << info.str() << " port settings changed, nData1: " << nData1 << " nData2: " << nData2;
        } else if (OMX_EventBufferFlag == eEvent) {
            LOGI << info.str() << " buffer flag changed, nData1: " << nData1 << " nData2: " << nData2;
            if (OMX_BUFFERFLAG_EOS == nData2) {
                tsem_up(event_sem_);
            }
        } else {
            LOGI << info.str() << " unknown event type: " << GetEnumName(eEvent) << " nData1: " << nData1 << " nData2: " << nData2;
        }
        return VEventHandler(hComponent, eEvent, nData1, nData2, pEventData);
    }
    OMX_ERRORTYPE OMXCallbackBase::BaseEmptyBufferDone(OMX_HANDLETYPE hComponent, OMX_BUFFERHEADERTYPE *pBuffer) {
//        std::stringstream info;
//        info << "Component: " << component_name_ << " of handle: " << hComponent;
//        LOGI << "EmptyBufferDone for " << info.str() << ", pBuffer: " << pBuffer;
        if (app_ctx_->IsEos()) {
            LOGI << "End of stream: " << pBuffer->nFlags << ", dropping empty this buffer.";
            return OMX_ErrorNone;
        }

//        // read the next buffer
//        app_ctx_->FillBuffer(pBuffer);
//
//        auto err = OMX_EmptyThisBuffer(hComponent, pBuffer);
//        if (err != OMX_ErrorNone) {
//            LOGE << "Empty buffer failed: " << GetEnumName(err);
//            return err;
//        }
        return OMX_ErrorNone;
    }
    OMX_ERRORTYPE OMXCallbackBase::BaseFillBufferDone(OMX_HANDLETYPE hComponent, OMX_BUFFERHEADERTYPE *pBuffer) {
        std::stringstream info;
        //        auto comp = static_cast<OMX_COMPONENTTYPE *>(hComponent);
        info << "Component: " << component_name_ << " of handle: " << hComponent;
        //        LOGI << "FillBufferDone for " << info.str() << ", pBuffer: " << pBuffer;
        if (nullptr == pBuffer) {
            LOGW << "Ouch! buffer is nullptr to output...";
            return OMX_ErrorNone;
        }

        if (app_ctx_->IsEos()) {
            LOGI << "End of stream: " << pBuffer->nFlags << ", dropping empty this buffer.";
            return OMX_ErrorNone;
        }

        LOGI << "Output buffer: " << pBuffer << ", pBuffer->nFilledLen: " << pBuffer->nFilledLen
             << " buffer output index: " << pBuffer->nOutputPortIndex << ", timestamp: " << pBuffer->nTimeStamp;
        // write file...
        pBuffer->nFilledLen = 0;

        if (pBuffer->nFlags & OMX_BUFFERFLAG_EOS) {
            app_ctx_->SetEos(OMX_TRUE);
            return OMX_ErrorNone;
        }

        auto err = OMX_FillThisBuffer(hComponent, pBuffer);
        if (err != OMX_ErrorNone) {
            LOGW << "Fill buffer failed: " << GetEnumName(err) << " for component handle: " << hComponent;
            return err;
        }
        return OMX_ErrorNone;
    }

}// namespace openmax::callback
